/*
* Copyright (c) 2006-2007 Erin Catto http:
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked, and must not be
* misrepresented the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/

goog.provide('box2d.ContactManager');

goog.require('box2d.Contact');
goog.require('box2d.ContactFactory');
goog.require('box2d.NullContact');
goog.require('box2d.PairCallback');

/**
 @constructor
 @extends {box2d.PairCallback}
 @param {!box2d.World} world
 */
box2d.ContactManager = function(world) {
  // The constructor for box2d.PairCallback
  //
  // initialize instance variables for references
  this.m_nullContact = new box2d.NullContact();
  //
  /**
   @private
   @type {!box2d.World}
   */
  this.m_world = world;
  this.m_destroyImmediate = false;
};
goog.inherits(box2d.ContactManager, box2d.PairCallback);

/**
  // This is a callback from the broadphase when two AABB proxies begin
  // to overlap. We create a box2d.Contact to manage the narrow phase.
  @param {!box2d.Shape} proxyUserData1
  @param {!box2d.Shape} proxyUserData2
  @return {!box2d.Contact}
*/
box2d.ContactManager.prototype.PairAdded = function(proxyUserData1, proxyUserData2) {
  var shape1 = proxyUserData1;
  var shape2 = proxyUserData2;

  var body1 = shape1.m_body;
  var body2 = shape2.m_body;

  if (body1.IsStatic() && body2.IsStatic()) {
    return this.m_nullContact;
  }

  if (shape1.m_body == shape2.m_body) {
    return this.m_nullContact;
  }

  if (body2.IsConnected(body1)) {
    return this.m_nullContact;
  }

  if (this.m_world.collisionFilter.ShouldCollide(shape1, shape2) == false) {
    return this.m_nullContact;
  }

  // Ensure that body2 is dynamic (body1 is static or dynamic).
  if (body2.m_invMass == 0.0) {
    var tempShape = shape1;
    shape1 = shape2;
    shape2 = tempShape;
    var tempBody = body1;
    body1 = body2;
    body2 = tempBody;
  }

  // Call the factory.
  var contact = box2d.ContactFactory.Create(shape1, shape2);

  if (contact == null) {
    return this.m_nullContact;
  } else {
    // Insert into the world.
    contact.m_prev = null;
    contact.m_next = this.m_world.m_contactList;
    if (this.m_world.m_contactList != null) {
      this.m_world.m_contactList.m_prev = contact;
    }
    this.m_world.m_contactList = contact;
    this.m_world.m_contactCount++;
  }

  return contact;
};

// This is a callback from the broadphase when two AABB proxies cease
// to overlap. We destroy the box2d.Contact.
box2d.ContactManager.prototype.PairRemoved = function(proxyUserData1, proxyUserData2, pairUserData) {

  if (pairUserData == null) {
    return;
  }

  var c = pairUserData;
  if (c != this.m_nullContact) {
    //box2d.Settings.b2Assert(this.m_world.m_contactCount > 0);
    if (this.m_destroyImmediate == true) {
      this.DestroyContact(c);
      c = null;
    } else {
      c.m_flags |= box2d.Contact.e_destroyFlag;
    }
  }
};

box2d.ContactManager.prototype.DestroyContact = function(c) {

  //box2d.Settings.b2Assert(this.m_world.m_contactCount > 0);
  // Remove from the world.
  if (c.m_prev) {
    c.m_prev.m_next = c.m_next;
  }

  if (c.m_next) {
    c.m_next.m_prev = c.m_prev;
  }

  if (c == this.m_world.m_contactList) {
    this.m_world.m_contactList = c.m_next;
  }

  // If there are contact points, then disconnect from the island graph.
  if (c.GetManifoldCount() > 0) {
    var body1 = c.m_shape1.m_body;
    var body2 = c.m_shape2.m_body;
    var node1 = c.m_node1;
    var node2 = c.m_node2;

    // Wake up touching bodies.
    body1.WakeUp();
    body2.WakeUp();

    // Remove from body 1
    if (node1.prev) {
      node1.prev.next = node1.next;
    }

    if (node1.next) {
      node1.next.prev = node1.prev;
    }

    if (node1 == body1.m_contactList) {
      body1.m_contactList = node1.next;
    }

    node1.prev = null;
    node1.next = null;

    // Remove from body 2
    if (node2.prev) {
      node2.prev.next = node2.next;
    }

    if (node2.next) {
      node2.next.prev = node2.prev;
    }

    if (node2 == body2.m_contactList) {
      body2.m_contactList = node2.next;
    }

    node2.prev = null;
    node2.next = null;
  }

  --this.m_world.m_contactCount;
};

// Destroy any contacts marked for deferred destruction.
box2d.ContactManager.prototype.CleanContactList = function() {
  var c = this.m_world.m_contactList;
  while (c != null) {
    var c0 = c;
    c = c.m_next;

    if (c0.m_flags & box2d.Contact.e_destroyFlag) {
      this.DestroyContact(c0);
      c0 = null;
    }
  }
};

// This is the top level collision call for the time step. Here
// all the narrow phase collision is processed for the world
// contact list.
box2d.ContactManager.prototype.Collide = function() {
  var body1;
  var body2;
  var node1;
  var node2;

  for (var c = this.m_world.m_contactList; c != null; c = c.m_next) {
    if (c.m_shape1.m_body.IsSleeping() && c.m_shape2.m_body.IsSleeping()) {
      continue;
    }

    var oldCount = c.GetManifoldCount();
    c.Evaluate();

    var newCount = c.GetManifoldCount();

    if (oldCount == 0 && newCount > 0) {
      //box2d.Settings.b2Assert(c.GetManifolds().pointCount > 0);
      // Connect to island graph.
      body1 = c.m_shape1.m_body;
      body2 = c.m_shape2.m_body;
      node1 = c.m_node1;
      node2 = c.m_node2;

      // Connect to body 1
      node1.contact = c;
      node1.other = body2;

      node1.prev = null;
      node1.next = body1.m_contactList;
      if (node1.next != null) {
        node1.next.prev = c.m_node1;
      }
      body1.m_contactList = c.m_node1;

      // Connect to body 2
      node2.contact = c;
      node2.other = body1;

      node2.prev = null;
      node2.next = body2.m_contactList;
      if (node2.next != null) {
        node2.next.prev = node2;
      }
      body2.m_contactList = node2;
    } else if (oldCount > 0 && newCount == 0) {
      // Disconnect from island graph.
      body1 = c.m_shape1.m_body;
      body2 = c.m_shape2.m_body;
      node1 = c.m_node1;
      node2 = c.m_node2;

      // Remove from body 1
      if (node1.prev) {
        node1.prev.next = node1.next;
      }

      if (node1.next) {
        node1.next.prev = node1.prev;
      }

      if (node1 == body1.m_contactList) {
        body1.m_contactList = node1.next;
      }

      node1.prev = null;
      node1.next = null;

      // Remove from body 2
      if (node2.prev) {
        node2.prev.next = node2.next;
      }

      if (node2.next) {
        node2.next.prev = node2.prev;
      }

      if (node2 == body2.m_contactList) {
        body2.m_contactList = node2.next;
      }

      node2.prev = null;
      node2.next = null;
    }
  }
};
